use crate::{
    config::{Config, IVerge},
    core::{CoreManager, handle, hotkey, sysopt, tray},
    logging_error,
    module::lightweight,
    utils::{draft::SharedBox, logging::Type},
};
use anyhow::Result;
use serde_yaml_ng::Mapping;

/// Patch Clash configuration
pub async fn patch_clash(patch: Mapping) -> Result<()> {
    Config::clash()
        .await
        .edit_draft(|d| d.patch_config(patch.clone()));

    let res = {
        // 激活订阅
        if patch.get("secret").is_some() || patch.get("external-controller").is_some() {
            Config::generate().await?;
            CoreManager::global().restart_core().await?;
        } else {
            if patch.get("mode").is_some() {
                logging_error!(Type::Tray, tray::Tray::global().update_menu().await);
                logging_error!(Type::Tray, tray::Tray::global().update_icon().await);
            }
            Config::runtime()
                .await
                .edit_draft(|d| d.patch_config(patch));
            CoreManager::global().update_config().await?;
        }
        handle::Handle::refresh_clash();
        <Result<()>>::Ok(())
    };
    match res {
        Ok(()) => {
            Config::clash().await.apply();
            // 分离数据获取和异步调用
            let clash_data = Config::clash().await.data_arc();
            clash_data.save_config().await?;
            Ok(())
        }
        Err(err) => {
            Config::clash().await.discard();
            Err(err)
        }
    }
}

// Define update flags as bitflags for better performance
#[derive(Clone, Copy)]
enum UpdateFlags {
    None = 0,
    RestartCore = 1 << 0,
    ClashConfig = 1 << 1,
    VergeConfig = 1 << 2,
    Launch = 1 << 3,
    SysProxy = 1 << 4,
    SystrayIcon = 1 << 5,
    Hotkey = 1 << 6,
    SystrayMenu = 1 << 7,
    SystrayTooltip = 1 << 8,
    SystrayClickBehavior = 1 << 9,
    LighteWeight = 1 << 10,
}

fn determine_update_flags(patch: &IVerge) -> i32 {
    let mut update_flags: i32 = UpdateFlags::None as i32;

    let tun_mode = patch.enable_tun_mode;
    let auto_launch = patch.enable_auto_launch;
    let system_proxy = patch.enable_system_proxy;
    let pac = patch.proxy_auto_config;
    let pac_content = &patch.pac_file_content;
    let proxy_bypass = &patch.system_proxy_bypass;
    let language = &patch.language;
    let mixed_port = patch.verge_mixed_port;
    #[cfg(target_os = "macos")]
    let tray_icon = &patch.tray_icon;
    #[cfg(not(target_os = "macos"))]
    let tray_icon: Option<String> = None;
    let common_tray_icon = patch.common_tray_icon;
    let sysproxy_tray_icon = patch.sysproxy_tray_icon;
    let tun_tray_icon = patch.tun_tray_icon;
    #[cfg(not(target_os = "windows"))]
    let redir_enabled = patch.verge_redir_enabled;
    #[cfg(not(target_os = "windows"))]
    let redir_port = patch.verge_redir_port;
    #[cfg(target_os = "linux")]
    let tproxy_enabled = patch.verge_tproxy_enabled;
    #[cfg(target_os = "linux")]
    let tproxy_port = patch.verge_tproxy_port;
    let socks_enabled = patch.verge_socks_enabled;
    let socks_port = patch.verge_socks_port;
    let http_enabled = patch.verge_http_enabled;
    let http_port = patch.verge_port;
    let enable_tray_speed = patch.enable_tray_speed;
    // let enable_tray_icon = patch.enable_tray_icon;
    let enable_global_hotkey = patch.enable_global_hotkey;
    let tray_event = &patch.tray_event;
    let home_cards = patch.home_cards.clone();
    let enable_auto_light_weight = patch.enable_auto_light_weight_mode;
    let enable_external_controller = patch.enable_external_controller;
    let tray_inline_proxy_groups = patch.tray_inline_proxy_groups;

    if tun_mode.is_some() {
        update_flags |= UpdateFlags::ClashConfig as i32;
        update_flags |= UpdateFlags::SystrayMenu as i32;
        update_flags |= UpdateFlags::SystrayTooltip as i32;
        update_flags |= UpdateFlags::SystrayIcon as i32;
    }
    if enable_global_hotkey.is_some() || home_cards.is_some() {
        update_flags |= UpdateFlags::VergeConfig as i32;
    }
    #[cfg(not(target_os = "windows"))]
    if redir_enabled.is_some() || redir_port.is_some() {
        update_flags |= UpdateFlags::RestartCore as i32;
    }
    #[cfg(target_os = "linux")]
    if tproxy_enabled.is_some() || tproxy_port.is_some() {
        update_flags |= UpdateFlags::RestartCore as i32;
    }
    if socks_enabled.is_some()
        || http_enabled.is_some()
        || socks_port.is_some()
        || http_port.is_some()
        || mixed_port.is_some()
    {
        update_flags |= UpdateFlags::RestartCore as i32;
    }
    if auto_launch.is_some() {
        update_flags |= UpdateFlags::Launch as i32;
    }

    if system_proxy.is_some() {
        update_flags |= UpdateFlags::SysProxy as i32;
        update_flags |= UpdateFlags::SystrayMenu as i32;
        update_flags |= UpdateFlags::SystrayTooltip as i32;
        update_flags |= UpdateFlags::SystrayIcon as i32;
    }

    if proxy_bypass.is_some() || pac_content.is_some() || pac.is_some() {
        update_flags |= UpdateFlags::SysProxy as i32;
    }

    if language.is_some() {
        update_flags |= UpdateFlags::SystrayMenu as i32;
    }
    if common_tray_icon.is_some()
        || sysproxy_tray_icon.is_some()
        || tun_tray_icon.is_some()
        || tray_icon.is_some()
        || enable_tray_speed.is_some()
    // || enable_tray_icon.is_some()
    {
        update_flags |= UpdateFlags::SystrayIcon as i32;
    }

    if patch.hotkeys.is_some() {
        update_flags |= UpdateFlags::Hotkey as i32;
        update_flags |= UpdateFlags::SystrayMenu as i32;
    }

    if tray_event.is_some() {
        update_flags |= UpdateFlags::SystrayClickBehavior as i32;
    }

    if enable_auto_light_weight.is_some() {
        update_flags |= UpdateFlags::LighteWeight as i32;
    }

    if enable_external_controller.is_some() {
        update_flags |= UpdateFlags::RestartCore as i32;
    }

    if tray_inline_proxy_groups.is_some() {
        update_flags |= UpdateFlags::SystrayMenu as i32;
    }

    update_flags
}

async fn process_terminated_flags(update_flags: i32, patch: &IVerge) -> Result<()> {
    // Process updates based on flags
    if (update_flags & (UpdateFlags::RestartCore as i32)) != 0 {
        Config::generate().await?;
        CoreManager::global().restart_core().await?;
    }
    if (update_flags & (UpdateFlags::ClashConfig as i32)) != 0 {
        CoreManager::global().update_config().await?;
        handle::Handle::refresh_clash();
    }
    if (update_flags & (UpdateFlags::VergeConfig as i32)) != 0 {
        Config::verge()
            .await
            .edit_draft(|d| d.enable_global_hotkey = patch.enable_global_hotkey);
        handle::Handle::refresh_verge();
    }
    if (update_flags & (UpdateFlags::Launch as i32)) != 0 {
        sysopt::Sysopt::global().update_launch().await?;
    }
    if (update_flags & (UpdateFlags::SysProxy as i32)) != 0 {
        sysopt::Sysopt::global().update_sysproxy().await?;
    }
    if (update_flags & (UpdateFlags::Hotkey as i32)) != 0
        && let Some(hotkeys) = &patch.hotkeys
    {
        hotkey::Hotkey::global().update(hotkeys.to_owned()).await?;
    }
    if (update_flags & (UpdateFlags::SystrayMenu as i32)) != 0 {
        tray::Tray::global().update_menu().await?;
    }
    if (update_flags & (UpdateFlags::SystrayIcon as i32)) != 0 {
        tray::Tray::global().update_icon().await?;
    }
    if (update_flags & (UpdateFlags::SystrayTooltip as i32)) != 0 {
        tray::Tray::global().update_tooltip().await?;
    }
    if (update_flags & (UpdateFlags::SystrayClickBehavior as i32)) != 0 {
        tray::Tray::global().update_click_behavior().await?;
    }
    if (update_flags & (UpdateFlags::LighteWeight as i32)) != 0 {
        if patch.enable_auto_light_weight_mode.unwrap_or(false) {
            lightweight::enable_auto_light_weight_mode().await;
        } else {
            lightweight::disable_auto_light_weight_mode();
        }
    }
    Ok(())
}

pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> {
    Config::verge().await.edit_draft(|d| d.patch_config(patch));

    let update_flags = determine_update_flags(patch);
    let process_flag_result: std::result::Result<(), anyhow::Error> = {
        process_terminated_flags(update_flags, patch).await?;
        Ok(())
    };

    if let Err(err) = process_flag_result {
        Config::verge().await.discard();
        return Err(err);
    }
    Config::verge().await.apply();
    if !not_save_file {
        // 分离数据获取和异步调用
        let verge_data = Config::verge().await.data_arc();
        verge_data.save_file().await?;
    }
    Ok(())
}

pub async fn fetch_verge_config() -> Result<SharedBox<IVerge>> {
    let draft = Config::verge().await;
    let data = draft.data_arc();
    Ok(data)
}
